const getParentScroll = (el) => {
  let parent = el.parentNode;
  while(parent) {
    if(/(scroll)|(auto)/.test(getComputedStyle(parent)['overflow'])) {
      return parent;
    }
    parent = parent.parentNode;
  }
  return parent;
}

const loadImg = (src, resolve, reject) => {
  const Img = new Image();
  Img.src = src;
  Img.onload = resolve;
  Img.onerror = reject;
}

function Lazy (Vue) {

  class ReactiveListener {
    constructor({el, src, options, elRender}) {
      this.el = el;
      this.src = src;
      this.options = options;
      this.elRender = elRender;
      this.isLoad = false;
      this.status = 'loading'; // 默认都是loading状态
    }
    // 判断是否在可视区
    checkView() {
      let {top} = this.el.getBoundingClientRect();

      return top < window.innerHeight * (this.options.preLoad || 1.3);
    }
    // 加载图片
    load() {
      this.elRender(this, 'loading');
      loadImg(this.src, () => {
        // 图片加载成功的回调
        this.status = 'loaded';
        this.isLoad = true;
        this.elRender(this, 'loaded');

      }, () => {
        // 图片加载失败的回调
        this.status = 'error';
        this.isLoad = true;
        this.elRender(this, 'error');
      })

    }
  }

  return class LazyClass {
    constructor(options) {
      this.options = options;
      this.isBind = false;
      this.listenerQueue = [];
    }
    add(el, bindings, vnode) {
      // console.log(el, bindings, vnode);
      Vue.nextTick(() => {
        // 给父级带有overflow： auto或 scroll的元素添加滚动监听
        const scrollParent = getParentScroll(el);
        if(scrollParent && !this.isBind) {
          this.isBind = true;
          scrollParent.addEventListener('scroll', this.handleLazyLoad.bind(this))
        }
        const listener = new ReactiveListener({
          el,
          src: bindings.value,
          options: this.options,
          elRender: this.elRender.bind(this)
        });
        this.listenerQueue.push(listener);
        this.handleLazyLoad();
      })
    }
    handleLazyLoad() {
      // 这里判断预判是否是在可视区内
      this.listenerQueue.forEach(listener => {
        if(!listener.isLoad) {
          let catIn = listener.checkView();
          catIn && listener.load();
        }
      })
    }
    elRender(listener, status) {
      let el = listener.el;
      let src = '';
      switch(status) {
        case 'loading':
          src = listener.options.loading || '';
          break;
        case 'error':
          src = listener.options.error || '';
          break;
        default:
          src = listener.src;
          break;
      }
      el.setAttribute('src', src);
    }
  }

}

const VueLazyload = {
  install(Vue, options) {
    const lazyClass = Lazy(Vue);
    const lazy = new lazyClass(options);
    Vue.directive('lazy', {
      bind:lazy.add.bind(lazy)
    })
  }
}